<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>TODO List</title>

    <!-- DevTools -->
    <script src="http://localhost:8097"></script>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/immutable@4.0.0-rc.12/dist/immutable.js"></script>

    <!-- Don't use this in production: -->
    <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

    <style type="text/css">
      .Input {
        font-size: 1rem;
        padding: 0.25rem;
      }

      .IconButton {
        padding: 0.25rem;
        border: none;
        background: none;
        cursor: pointer;
      }

      .List {
        margin: 0.5rem 0 0;
        padding: 0;
      }

      .ListItem {
        list-style-type: none;
      }

      .Label {
        cursor: pointer;
        padding: 0.25rem;
        color: #555;
      }
      .Label:hover {
        color: #000;
      }

      .IconButton {
        padding: 0.25rem;
        border: none;
        background: none;
        cursor: pointer;
      }
    </style>
  </head>
  <body>
    <div id="root"></div>
    <script type="text/babel">
      const { Fragment, useCallback, useState } = React;

      function List(props) {
        const [newItemText, setNewItemText] = useState("");
        const [items, setItems] = useState([
          { id: 1, isComplete: true, text: "First" },
          { id: 2, isComplete: true, text: "Second" },
          { id: 3, isComplete: false, text: "Third" }
        ]);
        const [uid, setUID] = useState(4);

        const handleClick = useCallback(() => {
          if (newItemText !== "") {
            setItems([
              ...items,
              {
                id: uid,
                isComplete: false,
                text: newItemText
              }
            ]);
            setUID(uid + 1);
            setNewItemText("");
          }
        }, [newItemText, items, uid]);

        const handleKeyPress = useCallback(
          event => {
            if (event.key === "Enter") {
              handleClick();
            }
          },
          [handleClick]
        );

        const handleChange = useCallback(
          event => {
            setNewItemText(event.currentTarget.value);
          },
          [setNewItemText]
        );

        const removeItem = useCallback(
          itemToRemove => setItems(items.filter(item => item !== itemToRemove)),
          [items]
        );

        const toggleItem = useCallback(
          itemToToggle => {
            const index = items.indexOf(itemToToggle);

            setItems(
              items
                .slice(0, index)
                .concat({
                  ...itemToToggle,
                  isComplete: !itemToToggle.isComplete
                })
                .concat(items.slice(index + 1))
            );
          },
          [items]
        );

        return (
          <Fragment>
            <h1>List</h1>
            <input
              type="text"
              placeholder="New list item..."
              className="Input"
              value={newItemText}
              onChange={handleChange}
              onKeyPress={handleKeyPress}
            />
            <button
              className="IconButton"
              disabled={newItemText === ""}
              onClick={handleClick}
            >
              <span role="img" aria-label="Add item">
                ➕
              </span>
            </button>
            <ul className="List">
              {items.map(item => (
                <ListItem
                  key={item.id}
                  item={item}
                  removeItem={removeItem}
                  toggleItem={toggleItem}
                />
              ))}
            </ul>
          </Fragment>
        );
      }

      function ListItem({ item, removeItem, toggleItem }) {
        const handleDelete = useCallback(() => {
          removeItem(item);
        }, [item, removeItem]);

        const handleToggle = useCallback(() => {
          toggleItem(item);
        }, [item, toggleItem]);

        return (
          <li className="ListItem">
            <button className="IconButton" onClick={handleDelete}>
              🗑
            </button>
            <label className="Label">
              <input
                className="Input"
                checked={item.isComplete}
                onChange={handleToggle}
                type="checkbox"
              />{" "}
              {item.text}
            </label>
          </li>
        );
      }

      function SimpleValues() {
        return (
          <ChildComponent
            string="abc"
            emptyString=""
            number={123}
            undefined={undefined}
            null={null}
            nan={NaN}
            infinity={Infinity}
            true={true}
            false={false}
          />
        );
      }

      class Custom {
        _number = 42;
        get number() {
          return this._number;
        }
      }

      function CustomObject() {
        return <ChildComponent customObject={new Custom()} />;
      }

      const object = {
        string: "abc",
        longString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKJLMNOPQRSTUVWXYZ1234567890",
        emptyString: "",
        number: 123,
        boolean: true,
        undefined: undefined,
        null: null
      };

      function ObjectProps() {
        return (
          <ChildComponent
            object={{
              outer: {
                inner: object
              }
            }}
            array={["first", "second", "third"]}
            objectInArray={[object]}
            arrayInObject={{ array: ["first", "second", "third"] }}
            deepObject={{
              // Known limitation: we won't go deeper than several levels.
              // In the future, we might offer a way to request deeper access on demand.
              a: {
                b: {
                  c: {
                    d: {
                      e: {
                        f: {
                          g: {
                            h: {
                              i: {
                                j: 10
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }}
          />
        );
      }

      const set = new Set(['abc', 123]);
      const map = new Map([['name', 'Brian'], ['food', 'sushi']]);
      const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
      const mapOfMaps = new Map([['first', map], ['second', map]]);
      const typedArray = Int8Array.from([100, -100, 0]);
      const immutable = Immutable.fromJS({
        a: [{ hello: 'there' }, 'fixed', true],
        b: 123,
        c: {
          '1': 'xyz',
          xyz: 1,
        },
      });

      function UnserializableProps() {
        return (
          <ChildComponent
            map={map}
            set={set}
            mapOfMaps={mapOfMaps}
            setOfSets={setOfSets}
            typedArray={typedArray}
            immutable={immutable}
          />
        );
      }

      function ChildComponent(props: any) {
        return null;
      }

      function InspectableElements() {
        return (
          <Fragment>
            <SimpleValues />
            <ObjectProps />
            <UnserializableProps />
            <CustomObject />
          </Fragment>
        );
      }

      function App() {
        return (
          <Fragment>
            <List />
            <InspectableElements />
          </Fragment>
        );
      }

      ReactDOM.render(<App />, document.getElementById("root"));
    </script>
  </body>
</html>
